package com.sh.mytool.cv.dao.impl;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.stereotype.Repository;
import com.sh.common.page.PageBean;
import com.sh.common.page.PageUtils;
import com.sh.common.page.QueryResult;
import com.sh.mytool.cv.dao.BasicDao;

@Repository("basicDao")
public class BasicDaoImpl<T> implements BasicDao<T> {

	@Resource(name="jpaTemplate")
	private JpaTemplate jpaTemplate;

	public PageBean findPage(final String sql, final Object[] params, final String resultMappingName, int currentPage, int pageSize) {
		String database = "mysql";
		String countSql = PageUtils.getPageCountSql(sql, database);
		int totalCount = getCount(countSql, params);
		PageBean pageBean = new PageBean(totalCount, pageSize, currentPage);
		final int first = pageBean.getStartOfPage()-1;
		final int max = pageBean.getPageSize();
		List<?> list = (List<?>) jpaTemplate.execute(new JpaCallback<Object>() {
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createNativeQuery(sql, resultMappingName);
				if(params != null){
					for(int i=0, len=params.length; i<len; i++){
						query.setParameter(i+1, params[i]);
					}
				}
				query.setFirstResult(first);
				query.setMaxResults(max);
				return query.getResultList();
			}
		});
		pageBean.setList(list);
		return pageBean;
	}

	public int getCount(final String sql, final Object[] params){
		int result = 0;
		Object oresult = jpaTemplate.execute(new JpaCallback<Object>() {
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createNativeQuery(sql);
				if(params != null){
					for(int i=0, len=params.length; i<len; i++){
						query.setParameter(i+1, params[i]);
					}
				}
				return query.getSingleResult();
			}
		});
		if(oresult != null){
			result = Integer.parseInt(oresult.toString());
		}
		return result;
	}

	public List<?> findAll(final String sql, final Object[] params, final String resultMappingName) {
		return (List<?>) jpaTemplate.execute(new JpaCallback<Object>() {
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createNativeQuery(sql, resultMappingName);
				if(params != null){
					for(int i=0, len=params.length; i<len; i++){
						query.setParameter(i+1, params[i]);
					}
				}
				return query.getResultList();
			}
		});
		// TODO: 为什么下面会报关联不到事务的异常
//		EntityManagerFactory emFactory = jpaTemplate.getEntityManagerFactory();
//		EntityManager em = emFactory.createEntityManager();
//		System.out.println(em);
//		Query query = em.createNativeQuery(sql);
//		return query.getResultList();
	}

	public T findById(final Class<T> entityClass, final Object entityId) {
		T result = null;
		if(entityId != null){
			result = jpaTemplate.find(entityClass, entityId);
		}
		return result;
	}
	
	public Object getSingleResult(final String jpaSql, final Object[] params) {
		return jpaTemplate.execute(new JpaCallback<Object>() {
			public Object doInJpa(EntityManager em) throws PersistenceException {
				Query query = em.createQuery(jpaSql);
				if(params != null){
					for(int i=0, len=params.length; i<len; i++){
						query.setParameter(i+1, params[i]);
					}
				}
				return query.getSingleResult();
			}
		});
	}

	public void insert(final T entity){
		jpaTemplate.persist(entity);
	}

	// 需要注意：先要获得原先合并才可以
	public void update(final T entity){
		// 按照规范在service里面的事务写，不用merge也行
		jpaTemplate.merge(entity);
	}

	public void delete(final T entity){
		jpaTemplate.remove(entity);
	}

	public void deleteById(final Class<T> entityClass, final Object entityId) {
		// 托管状态下才能删除
		T entity = this.findById(entityClass, entityId);
		this.delete(entity);
	}

	public void batchExcute(final List<T> entitys){
//		for(T entity : entitys){
//			jpaTemplate.
//		}

	}

	// 提交（调用flush）分为2种方式：
	// AUTO:自动提交，实体管理器会在适当的时机同步实际记录到数据库，也是默认的提交方式。
	// COMMIT：一旦一个事务完毕了，那么就立刻提交到数据库（忽略事务共享、事务传播）。
	// 很多人建议使用默认的AUTO。
	public void flush() {
		this.jpaTemplate.execute(new JpaCallback<Object>() {
            public Object doInJpa(EntityManager em) throws PersistenceException {
            	em.getTransaction().commit();
                return null;
            }
        });
	}

	public JpaTemplate getJpaTemplate() {
		return jpaTemplate;
	}

	public void setJpaTemplate(JpaTemplate jpaTemplate) {
		this.jpaTemplate = jpaTemplate;
	}

	public QueryResult<T> getScrollData(final Class<T> entityClass, final LinkedHashMap<String, ?> where, final LinkedHashMap<String, String> orderby) {
		return getScrollData(entityClass, -1, -1, where, orderby);
	}

	@SuppressWarnings("unchecked")
	public QueryResult<T> getScrollData(final int firstIndex, final int maxResult, final String jpaSql){
		QueryResult<T> qr = new QueryResult<T>();
		List<?> resultList = (List<?>)jpaTemplate.execute(new JpaCallback<Object>(){
			public Object doInJpa(final EntityManager em) throws PersistenceException {
				Query query = em.createQuery(jpaSql);
				if(firstIndex != -1 && maxResult != -1){
					query.setFirstResult(firstIndex).setMaxResults(maxResult);
				}
				return query.getResultList();
			}
		});
		Long totalRecord = (Long)jpaTemplate.execute(new JpaCallback<Object>(){
			public Object doInJpa(final EntityManager em) throws PersistenceException {
				Query query = em.createQuery("SELECT COUNT(o) FROM (" + jpaSql + ") o");
				return query.getSingleResult();
			}
		});
		qr.setResultList((List<T>)resultList);
		qr.setTotalRecord(totalRecord);
		return qr;
	}
	
	@SuppressWarnings("unchecked")
	public QueryResult<T> getScrollData(final Class<T> entityClass, final int firstIndex, final int maxResult, final LinkedHashMap<String, ?> where, final LinkedHashMap<String, String> orderby) {
		QueryResult<T> qr = new QueryResult<T>();
		final String tableName = getEntityTableName(entityClass);
		final String orderbySql = buildOrderby(orderby);
		final String whereSql = buildWhere(where);
		List<?> resultList = (List<?>)jpaTemplate.execute(new JpaCallback<Object>(){
			public Object doInJpa(final EntityManager em) throws PersistenceException {
				Query query = em.createQuery("SELECT o FROM " + tableName + " o" + whereSql + orderbySql);
				if(where != null && where.size()!=0){
					int i=0;
					for(Entry<String, ?> entry : where.entrySet()){
						query.setParameter(++i, entry.getValue());
					}
				}
				if(firstIndex != -1 && maxResult != -1){
					query.setFirstResult(firstIndex).setMaxResults(maxResult);
				}
				return query.getResultList();
			}
		});
		Long totalRecord = (Long)jpaTemplate.execute(new JpaCallback<Object>(){
			public Object doInJpa(final EntityManager em) throws PersistenceException {
				Query query = em.createQuery("SELECT COUNT(o) FROM " + tableName + " o" + whereSql);
				if(where != null && where.size()!=0){
					int i=0;
					for(Entry<String, ?> entry : where.entrySet()){
						query.setParameter(++i, entry.getValue());
					}
				}
				return query.getSingleResult();
			}
		});
		qr.setResultList((List<T>)resultList);
		qr.setTotalRecord(totalRecord);
		return qr;
	}

	protected String getEntityTableName(Class<T> entityClass){
		String entityName = entityClass.getSimpleName();
		// 查询语句是JPQL，需要的不是真正的表名
//		String tableName =  entityClass.getAnnotation(Table.class).name();
		String tableName =  entityClass.getAnnotation(Entity.class).name();
		if(tableName != null && !"".equals((tableName.trim()))){
			entityName = tableName;
		}
		return entityName;
	}

	protected String buildWhere(LinkedHashMap<String, ?> where){
		StringBuffer whereSql = new StringBuffer("");
		if(where != null && where.size() !=0 ){
			whereSql.append(" WHERE 1=1");
			for(Entry<String, ?> entry : where.entrySet()){
				whereSql.append(" AND o.").append(entry.getKey()).append("=").append("?");
			}
		}
		return whereSql.toString();
	}

	protected String buildOrderby(LinkedHashMap<String, String> orderby){
		StringBuffer orderbySql = new StringBuffer("");
		if(orderby != null && orderby.size() !=0 ){
			orderbySql.append(" ORDER BY");
			for(Entry<String, String> entry : orderby.entrySet()){
				orderbySql.append(" o.").append(entry.getKey()).append(" ").append(entry.getValue()).append(",");
			}
			orderbySql.deleteCharAt(orderbySql.length()-1);
		}
		return orderbySql.toString();
	}
}
